﻿using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.VisualBasic.FileIO;

namespace FBProject.BusinessLogic.FBImport
{
    public class FBCsvImport : IDisposable
    {
        private static readonly log4net.ILog Logger = log4net.LogManager.GetLogger(typeof(FBCsvImport));

        private TextFieldParser CsvParser { get; set; }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
        public FBCsvImport(string csvFileName)
        {
            this.CsvParser = new TextFieldParser(csvFileName)
                {
                    TextFieldType = FieldType.Delimited,
                    Delimiters = new[] { "," },
                    HasFieldsEnclosedInQuotes = true
                };
            _disposed = false;
        }

        #region IDisposable Implementation

        private bool _disposed;

        public void Dispose()
        {
            Dispose(true);

            // Use SupressFinalize in case a subclass
            // of this type implements a finalizer.
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            // If you need thread safety, use a lock around these 
            // operations, as well as in your methods that use the resource.
            if (!_disposed)
            {
                if (disposing)
                {
                    if (this.CsvParser != null)
                        this.CsvParser.Dispose();
                }

                // Indicate that the instance has been disposed.
                this.CsvParser = null;
                _disposed = true;
            }
        }

        #endregion

        public IDictionary<string, ExternalTask> ReadExternalTasksFromCsv()
        {
            var listOfExternalTasks = new Dictionary<string, ExternalTask>();

            var columnIndexes = this.GetCsvColumnIndexes();
            while (!this.CsvParser.EndOfData)
            {
                string[] fields;
                fields = this.CsvParser.ReadFields();

                var externalTask = GetExternalTask(columnIndexes, fields);

                listOfExternalTasks.Add(externalTask.TaskNumber, externalTask);
            }

            return listOfExternalTasks;
        }

        private static ExternalTask GetExternalTask(ExternalColumnIndexes columnIndexes, string[] fields)
        {
            var externalTask = new ExternalTask();
            externalTask.TaskNumber = fields[columnIndexes.TaskNumber];
            externalTask.Name = fields[columnIndexes.Name];
            double numericValue;
            if (Double.TryParse(fields[columnIndexes.Estimate], out numericValue))
            {
                externalTask.Estimate = (int)(numericValue * 60);
                externalTask.RemainingWork = externalTask.Estimate;
            }
            if (Double.TryParse(fields[columnIndexes.ElapsedTime], out numericValue))
            {
                externalTask.RemainingWork -= (int)(numericValue * 60);
            }
            externalTask.Priority = GetPriority(fields[columnIndexes.Priority]);
            externalTask.Completed = !fields[columnIndexes.Status].ToUpperInvariant().StartsWith("active", StringComparison.OrdinalIgnoreCase);
            externalTask.Milestone = GetMilestone(fields[columnIndexes.Milestone]);
            externalTask.Resource = fields[columnIndexes.Resource];
            return externalTask;
        }

        private static short GetPriority(string externalPriority)
        {
            short priority;
            if (!string.IsNullOrEmpty(externalPriority) && short.TryParse(externalPriority.Substring(0, 1), out priority))
            {
                return (short)((10 - priority) * 100);
            }
            Logger.WarnFormat("GetPriority() - Cannot convert priority \"{0}\"", externalPriority);
            return 0;
        }

        private static string GetMilestone(string externalMilestone)
        {
            int position = externalMilestone.IndexOf(":", StringComparison.OrdinalIgnoreCase);
            if (position >= 0)
                return externalMilestone.Substring(0, position);
            else
                return externalMilestone;
        }

        private ExternalColumnIndexes GetCsvColumnIndexes()
        {
            var csvColumnIndexes = new ExternalColumnIndexes();

            if (!this.CsvParser.EndOfData)
            {
                string[] headers;
                headers = this.CsvParser.ReadFields();

                for (int columnIndex = 0; columnIndex <= headers.GetUpperBound(0); columnIndex++)
                {
                    var externalColumnName = headers[columnIndex].ToUpperInvariant();
                    if (ExternalColumnIndexes.PropertiesWithExternalName.ContainsKey(externalColumnName))
                    {
                        var propertyInfo = ExternalColumnIndexes.PropertiesWithExternalName[externalColumnName];
                        propertyInfo.SetValue(csvColumnIndexes, columnIndex, null);
                    }
                }
            }

            var missingColumns = csvColumnIndexes.GetMissingColumns();
            if ((missingColumns != null) && (missingColumns.Count > 0))
            {
                throw new MissingColumnsInSourceFileException(missingColumns);
            }

            return csvColumnIndexes;
        }

        public static string GetLatestFBCsvFile(string path, string filePattern)
        {
            var latestFBCsvFileName = string.Empty;
            var latestFBCsvFileCreationTime = DateTime.MinValue;

            if (Directory.Exists(path))
            {
                var fileNames = Directory.GetFiles(path, filePattern);
                if ((fileNames != null) && (fileNames.Length > 0))
                {
                    foreach (var fileName in fileNames)
                    {
                        var file = new FileInfo(fileName);
                        if (file.CreationTimeUtc > latestFBCsvFileCreationTime)
                        {
                            latestFBCsvFileName = fileName;
                            latestFBCsvFileCreationTime = file.CreationTimeUtc;
                        }
                    }
                }
            }
            else
            {
                Logger.WarnFormat("GetLatestFBCsvFile() - Path {0} doesn't exist", path);
            }

            return latestFBCsvFileName;
        }


    }
}
